#pragma once

#include "NamedElement.h"

#include "Dependency.h"
#include "Namespace.h"
#include "Package.h"
#include "StringExpression.h"

namespace uml {

const QString NamedElement::name_default_ = "";
const QString NamedElement::qualifiedName_default_ = "";
NamedElement::NamedElement(const QString& umlType)
    : Element(umlType) {}

void NamedElement::fromJson(const QJsonObject& json, QStringList& errorList) {
    if (json.isEmpty()) {
        return;
    }

    Element::fromJson(json, errorList);
    JsonSerializable::fromJsonArray(clientDependencies_, "clientDependencies", json, errorList);
    name_ = json.value("name").toString(name_default_);
    nameExpression_ = JsonSerializable::create<StringExpression>(json.value("nameExpression"), errorList);

    namespace2_ = JsonSerializable::create<Namespace>(json.value("namespace2"), errorList);

    qualifiedName_ = json.value("qualifiedName").toString(qualifiedName_default_);
    visibility_ = (VisibilityKind)json.value("visibility").toInt((int)visibility_);
}

void NamedElement::toJson(QJsonObject& json) {
    Element::toJson(json);
    JsonSerializable::toJsonArray(clientDependencies_, "clientDependencies", json);
    if (name_ != name_default_) {
        json.insert("name", name_);
    }
    if (nameExpression_) {
        QJsonObject nameExpression_json;
        nameExpression_->toJson(nameExpression_json);
        json.insert("nameExpression", nameExpression_json);
    }
    if (namespace2_) {
        QJsonObject namespace2_json;
        namespace2_->toJson(namespace2_json);
        json.insert("namespace2", namespace2_json);
    }
    if (qualifiedName_ != qualifiedName_default_) {
        json.insert("qualifiedName", qualifiedName_);
    }
    json.insert("visibility", (int)visibility_);
}

/// <summary>
/// allNamespaces
    /// The query allNamespaces() gives the sequence of Namespaces in which the
    /// NamedElement is nested, working outwards.
/// </summary>
    /// <code>
    /// result = (if owner.oclIsKindOf(TemplateParameter) and
    ///   owner.oclAsType(TemplateParameter).signature.template.oclIsKindOf(Namespace) then
    ///     let enclosingNamespace : Namespace =
    ///       owner.oclAsType(TemplateParameter).signature.template.oclAsType(Namespace) in
    ///         enclosingNamespace.allNamespaces()->prepend(enclosingNamespace)
    /// else
    ///   if namespace->isEmpty()
    ///     then OrderedSet{}
    ///   else
    ///     namespace.allNamespaces()->prepend(namespace)
    ///   endif
    /// endif)
    /// </code>
shared_ptr<vector<Namespace>> NamedElement::allNamespaces() const
{
    return nullptr;
}

/// <summary>
/// allOwningPackages
    /// The query allOwningPackages() returns the set of all the enclosing Namespaces of
    /// this NamedElement, working outwards, that are Packages, up to but not including
    /// the first such Namespace that is not a Package.
/// </summary>
    /// <code>
    /// result = (if namespace.oclIsKindOf(Package)
    /// then
    ///   let owningPackage : Package = namespace.oclAsType(Package) in
    ///     owningPackage->union(owningPackage.allOwningPackages())
    /// else
    ///   null
    /// endif)
    /// </code>
shared_ptr<vector<Package>> NamedElement::allOwningPackages() const
{
    return nullptr;
}

/// <summary>
/// isDistinguishableFrom
    /// The query isDistinguishableFrom() determines whether two NamedElements may
    /// logically co-exist within a Namespace. By default, two named elements are
    /// distinguishable if (a) they have types neither of which is a kind of the other
    /// or (b) they have different names.
/// </summary>
    /// <code>
    /// result = ((self.oclIsKindOf(n.oclType()) or n.oclIsKindOf(self.oclType())) implies
    ///     ns.getNamesOfMember(self)->intersection(ns.getNamesOfMember(n))->isEmpty()
    /// )
    /// </code>
bool NamedElement::isDistinguishableFrom(shared_ptr<NamedElement> n, shared_ptr<Namespace> ns) const
{
    return false;
}

/// <summary>
/// separator
    /// The query separator() gives the string that is used to separate names when
    /// constructing a qualifiedName.
/// </summary>
    /// <code>
    /// result = ('::')
    /// </code>
QString NamedElement::separator() const
{
    return "";
}

/// <summary>
/// clientDependencies getter
/// Indicates the Dependencies that reference this NamedElement as a client.
/// </summary>
vector<shared_ptr<Dependency>>& NamedElement::clientDependencies() {
    return clientDependencies_;
}

/// <summary>
/// name getter
/// The name of the NamedElement.
/// </summary>
QString NamedElement::name() const {
    return name_;
}

/// <summary>
/// name setter
/// The name of the NamedElement.
/// </summary>
void NamedElement::setName(const QString& value) {
    name_ = value;
}

/// <summary>
/// nameExpression getter
/// The StringExpression used to define the name of this NamedElement.
/// </summary>
shared_ptr<StringExpression> NamedElement::nameExpression() const {
    return nameExpression_;
}

/// <summary>
/// nameExpression setter
/// The StringExpression used to define the name of this NamedElement.
/// </summary>
void NamedElement::setNameExpression(shared_ptr<StringExpression> value) {
    nameExpression_ = value;
}

/// <summary>
/// namespace2 getter
/// Specifies the Namespace that owns the NamedElement.
/// </summary>
shared_ptr<Namespace> NamedElement::namespace2() const {
    return namespace2_;
}

/// <summary>
/// namespace2 setter
/// Specifies the Namespace that owns the NamedElement.
/// </summary>
void NamedElement::setNamespace2(shared_ptr<Namespace> value) {
    namespace2_ = value;
}

/// <summary>
/// qualifiedName getter
/// A name that allows the NamedElement to be identified within a hierarchy of
/// nested Namespaces. It is constructed from the names of the containing Namespaces
/// starting at the root of the hierarchy and ending with the name of the
/// NamedElement itself.
/// </summary>
QString NamedElement::qualifiedName() const {
    return qualifiedName_;
}

/// <summary>
/// qualifiedName setter
/// A name that allows the NamedElement to be identified within a hierarchy of
/// nested Namespaces. It is constructed from the names of the containing Namespaces
/// starting at the root of the hierarchy and ending with the name of the
/// NamedElement itself.
/// </summary>
void NamedElement::setQualifiedName(const QString& value) {
    qualifiedName_ = value;
}

/// <summary>
/// visibility getter
/// Determines whether and how the NamedElement is visible outside its owning
/// Namespace.
/// </summary>
VisibilityKind NamedElement::visibility() const {
    return visibility_;
}

/// <summary>
/// visibility setter
/// Determines whether and how the NamedElement is visible outside its owning
/// Namespace.
/// </summary>
void NamedElement::setVisibility(VisibilityKind value) {
    visibility_ = value;
}

} // namespace uml
